package com.lc.common.config;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.lc.common.exception.HandlerException;
import com.netflix.hystrix.exception.HystrixTimeoutException;
import feign.Response;
import feign.RetryableException;
import feign.Util;
import feign.codec.ErrorDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;

import java.io.IOException;
import java.nio.charset.Charset;

/**
 * 错误解码
 *
 * @author lc
 */
@Configuration
public class ExceptionDecoder implements ErrorDecoder {
    private static final Logger log = LoggerFactory.getLogger(ExceptionDecoder.class);

    @Value("${enableLog:true}")
    public boolean enableLog;

    private static final String UNKNOWN_ERROR = "服务器内部错误";

    @Override
    public Exception decode(String methodKey, Response response) {
        log.info("ExceptionDecoder class");
        log.info("status {} reading {}", response.status(), methodKey);
        switch (HttpStatus.valueOf(response.status())) {
            //400 404 503
            case BAD_REQUEST:
            case NOT_FOUND:
            case SERVICE_UNAVAILABLE:
                String logContent = String.format("错误原因：Feign调用：%s status: %s", methodKey, response.status());
                log.error(logContent);
                return new HandlerException(UNKNOWN_ERROR + (enableLog ? logContent : ""));
            //500
            case INTERNAL_SERVER_ERROR:
                return serverError(methodKey, response);
            default:
                break;
        }
        return new HandlerException(UNKNOWN_ERROR);
    }

    /**
     * 500 的处理
     *
     * @param methodKey 方法
     * @param response  返回
     * @return 自定义异常
     */
    private HandlerException serverError(String methodKey, Response response) {
        String body;
        try {
            if (response.body() != null) {
                body = Util.toString(response.body().asReader(Charset.defaultCharset()));
                JSONObject jsonObject = JSONUtil.parseObj(body);
                String logContent = String.format("错误原因：Feign调用：%s 状态: %s body %s", methodKey, response.status(), body);
                log.info(logContent);
                return new HandlerException(jsonObject.getStr("message") + (enableLog ? logContent : ""));
            }
        } catch (RetryableException retryableException) {
            String logContent = String.format("错误原因：Feign调用：%s 状态: %s 错误类型：RetryableException", methodKey, response.status());
            log.error(logContent);
            return new HandlerException(UNKNOWN_ERROR + (enableLog ? logContent : ""));
        } catch (IOException o) {
            String logContent = String.format("错误原因：Feign调用：%s 状态: %s 错误类型：IOException", methodKey, response.status());
            log.error(logContent);
            return new HandlerException(UNKNOWN_ERROR + (enableLog ? logContent : ""));
        }
        return new HandlerException(UNKNOWN_ERROR);
    }

    /**
     * 判断错误类型，返回一个可返回给前端的log
     *
     * @param cause     错误
     * @param className 类名称
     */
    public void check(final Throwable cause, final String className) {
        String logContent = "";

        //情况为已经调通的情况下，抛出了自定义异常
        if (cause instanceof HandlerException) {
            logContent = cause.getMessage();
        } else
            //熔断超时
            if (cause instanceof HystrixTimeoutException) {
                logContent = String.format("Feign服务调用失败，client:%s; 错误类型：HystrixTimeoutException；失败原因：%s", className, cause.getMessage());
            } else
                //重试异常
                if (cause instanceof RetryableException) {
                    logContent = String.format("Feign服务调用失败，client:%s;错误类型：RetryableException；失败原因%s", className, cause.getCause().getMessage());
                } else
                    //feign连接不通
                    if (cause instanceof RuntimeException) {
                        logContent = String.format("Feign服务调用失败，client:%s;错误类型：ClientException；失败原因%s", className, cause.getCause().getMessage());
                    } else {
                        //其他异常
                        logContent = String.format("Feign服务调用失败，client:%s;失败原因未知,cause:%s", className, cause.getMessage());
                    }
        log.error(logContent);
    }
}
